<template>
  <el-dialog title="流程绘制" :visible="visible" fullscreen destroy-on-close @close="close">
    <el-container>
      <el-header>
        <el-button-group>
          <el-button type="primary" @click="finish">完成</el-button>
          <el-button type="primary" @click="save">保存</el-button>
          <el-button type="primary" @click="upload">上传</el-button>
          <el-button type="primary" @click="undo">撤销</el-button>
          <el-button type="primary" @click="redo">重做</el-button>
          <el-button type="primary" @click="zoomOut">放大</el-button>
          <el-button type="primary" @click="zoomIn">缩小</el-button>
          <el-button type="primary" @click="reset">重置</el-button>
          <el-button type="primary" @click="downloadXml">导出流程定义</el-button>
          <el-button type="primary" @click="downloadSVG">导出流程图</el-button>
          <el-button type="primary" @click="close">关闭</el-button>
        </el-button-group>
      </el-header>
      <el-main>
        <el-container>
          <el-aside width="10%">
            <div class="containers">
              <div id="js-properties-panel" />
            </div>
          </el-aside>
          <el-main width="90%">
            <el-container>
              <el-aside width="70%">
                <div class="containers">
                  <div ref="canvas" class="canvas" />
                </div>
              </el-aside>
              <el-main width="30%">
                <panel
                  ref="propertyPanel"
                  :temp-process-definition-id=" tempProcessDefinitionId"
                  :process-definition-id=" processDefinitionId"
                />
              </el-main>
            </el-container>
          </el-main>
        </el-container>
      </el-main>
      <el-footer>
        <div v-highlight v-html="xmlViewData" />
      </el-footer>
    </el-container>
  </el-dialog>
</template>

<script>

// bpmn-js 设计器
import BpmnModeler from 'bpmn-js/lib/Modeler'
// 属性面板
import panel from './PropertyPanel'
import BpmData from './BpmData'
// xml格式化
import vkbeautify from 'vkbeautify'
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import camundaExtensionModule from 'camunda-bpmn-moddle/lib'
export default {
  components: {
    panel
  },
  data() {
    return {
      visible: false,
      // 模式
      mode: '',
      // xml浏览数据
      xmlViewData: '',
      // 流程定义标识
      processDefinitionId: '',
      // 当前流程定义版本号
      processDefinitionVersion: 1,
      // 当前流程定义编码
      processDefinitionKey: '',
      bpmnModeler: null,
      element: null,
      bpmData: new BpmData(),
      newScale: 1,
      bpmnXmlStr: `
      <?xml version="1.0" encoding="UTF-8"?>
      <bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="diagram" targetNamespace="OA" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" >
        <bpmn2:process id="defaultKey" name="新建流程" isExecutable="true" >
          <bpmn2:documentation>描述</bpmn2:documentation>
          <bpmn2:startEvent id="StartEvent_01ydzqe" name="开始">
            <bpmn2:outgoing>SequenceFlow_1qw929z</bpmn2:outgoing>
          </bpmn2:startEvent>
          <bpmn2:sequenceFlow id="SequenceFlow_1qw929z" sourceRef="StartEvent_01ydzqe" targetRef="Task_1piqdk6" />
          <bpmn2:userTask id="Task_1piqdk6" name="请假申请">
            <bpmn2:incoming>SequenceFlow_1qw929z</bpmn2:incoming>
            <bpmn2:outgoing>SequenceFlow_0ngnsbl</bpmn2:outgoing>
          </bpmn2:userTask>
          <bpmn2:userTask id="Task_10fqcwp" name="经理审批">
            <bpmn2:incoming>SequenceFlow_0ngnsbl</bpmn2:incoming>
            <bpmn2:outgoing>SequenceFlow_1xod8nh</bpmn2:outgoing>
          </bpmn2:userTask>
          <bpmn2:sequenceFlow id="SequenceFlow_1xod8nh" sourceRef="Task_10fqcwp" targetRef="EndEvent_0pnmjd3" />
          <bpmn2:endEvent id="EndEvent_0pnmjd3">
            <bpmn2:incoming>SequenceFlow_1xod8nh</bpmn2:incoming>
          </bpmn2:endEvent>
          <bpmn2:sequenceFlow id="SequenceFlow_0ngnsbl" sourceRef="Task_1piqdk6" targetRef="Task_10fqcwp" />
        </bpmn2:process>
        <bpmndi:BPMNDiagram id="BPMNDiagram_1">
          <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="defaultKey">
            <bpmndi:BPMNShape id="StartEvent_01ydzqe_di" bpmnElement="StartEvent_01ydzqe">
              <dc:Bounds x="532" y="72" width="36" height="36" />
              <bpmndi:BPMNLabel>
                <dc:Bounds x="539" y="53" width="22" height="14" />
              </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge id="SequenceFlow_1qw929z_di" bpmnElement="SequenceFlow_1qw929z">
              <di:waypoint x="550" y="108" />
              <di:waypoint x="550" y="150" />
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNShape id="UserTask_1qxjy46_di" bpmnElement="Task_1piqdk6">
              <dc:Bounds x="500" y="150" width="100" height="80" />
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape id="UserTask_18pwui1_di" bpmnElement="Task_10fqcwp">
              <dc:Bounds x="500" y="370" width="100" height="80" />
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge id="SequenceFlow_1xod8nh_di" bpmnElement="SequenceFlow_1xod8nh">
              <di:waypoint x="550" y="450" />
              <di:waypoint x="550" y="602" />
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNShape id="EndEvent_0pnmjd3_di" bpmnElement="EndEvent_0pnmjd3">
              <dc:Bounds x="532" y="602" width="36" height="36" />
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge id="SequenceFlow_0ngnsbl_di" bpmnElement="SequenceFlow_0ngnsbl">
              <di:waypoint x="550" y="230" />
              <di:waypoint x="550" y="370" />
            </bpmndi:BPMNEdge>
          </bpmndi:BPMNPlane>
        </bpmndi:BPMNDiagram>
      </bpmn2:definitions>
      `
    }
  },
  computed: {
    tempProcessDefinitionId() {
      return this.processDefinitionKey + ':' + (this.processDefinitionVersion + 1)
    }

  },
  watch: {
    bpmnXmlStr() {
      if (this.bpmnXmlStr) {
        // xml中存在尖括号，必须转义，否则无法正常按照html显示
        this.xmlViewData = '<pre><code class="xml">' + vkbeautify.xml(this.bpmnXmlStr).replace(/</ig, '&lt;').replace(/>/ig, '&gt;') + '</code></pre>'
      }
    }

  },

  methods: {
    close() {
      this.visible = false
    },
    // 新增
    add() {
      this.mode = 'add'
      this.visible = true
      this.$nextTick(function () {
        this.init()
      })
    },
    // 修改
    modify(id) {
      this.mode = 'modify'
      // 读取xml
      this.$api.workflow.workflowTemplate.getXml(id)
        .then(res => {
          this.bpmnXmlStr = res.data
          this.visible = true
          this.$nextTick(function () {
            this.init()
          })
        })
      // 模板数据
      this.$api.workflow.workflowTemplate.get(id)
        .then(res => {
          this.processDefinitionId = res.data.id
          this.processDefinitionKey = res.data.key
          this.processDefinitionVersion = res.data.version
          // 生成临时版本设置（仅当不存在时）
          this.$api.workflow.workflowTemplate.generateTempVersionConfig(this.processDefinitionId)
        })
    },
    // 完成
    finish() {
      // 工作流引擎根据key值自动处理新增问题，实际修改为版本升级形式的新增
      this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
        if (err) {
          this.$message({
            type: 'error',
            message: '保存出错，请重试'
          })
        } else {
          // 保存到服务器
          this.$api.workflow.workflowTemplate.add({ xml: xml })
            .then(() => {
              this.$emit('ok')
              this.close()
            }).finally(() => {
              this.loading = false
            })
        }
      })
    },
    // 保存
    save() {
      // 工作流引擎根据key值自动处理新增问题，实际修改为版本升级形式的新增
      this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
        if (err) {
          console.log('error:', err)
          this.$message({
            type: 'error',
            message: '保存出错，请重试'

          })
        } else {
          this.$message({
            type: 'info',
            message: '已保存'
          })
          this.bpmnXmlStr = xml
        }
      })
    },
    // 实例化流程模型
    init() {
      const canvas = this.$refs.canvas
      // 生成实例
      this.bpmnModeler = new BpmnModeler({
        container: canvas,
        additionalModules: [
          camundaExtensionModule

        ],
        moddleExtensions: {
          camunda: camundaModdleDescriptor
        }

      })
      // 生成流程图
      this.generateDiagram()
    },
    // 将字符串转换成图显示出来
    generateDiagram() {
      this.bpmnModeler.importXML(this.bpmnXmlStr, err => {
        if (err) {
          console.error(err)
        } else {
          this.$refs.propertyPanel.show(this.bpmnModeler)
        }
      })
    },
    // 撤销
    undo() {
      this.bpmnModeler.get('commandStack').undo()
    },
    // 重做
    redo() {
      this.bpmnModeler.get('commandStack').redo()
    },
    // 上传文件
    upload() {
      const vm = this
      const input = document.createElement('input')
      input.type = 'file'
      input.click()// 打开文件选择框
      input.onchange = function () {
        const file = input.files[0]
        if (window.FileReader) {
          try {
            const fr = new FileReader()
            fr.readAsText(file) // 将文件读取为文本
            fr.onload = function () {
              vm.bpmnXmlStr = fr.result
              vm.generateDiagram()
            }
          } catch (e) {
            alert(e.toString())
          }
        } else {
          alert('您的浏览器可能不支持此操作')
        }
      }
    },
    // 下载 XML 格式
    downloadXml() {
      this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
        if (err) {
          alert('导出错误，请重试')
        } else {
          this.download('xml', xml)
        }
      })
    },
    // 下载 SVG 格式
    downloadSVG() {
      this.bpmnModeler.saveSVG({ format: true }, (err, xml) => {
        if (err) {
          alert('导出错误，请重试')
        } else {
          this.download('svg', xml)
        }
      })
    },
    // 下载xml/svg
    download(type, data, name) {
      // 下载xml/svg
      let dataTrack = ''
      const a = document.createElement('a')
      switch (type) {
        case 'xml':
          dataTrack = 'bpmn'
          break
        case 'svg':
          dataTrack = 'svg'
          break
        default:
          break
      }
      const reName = name || `diagram.${dataTrack}`
      a.setAttribute(
        'href',
        `data:application/bpmn20-xml;charset=UTF-8,${encodeURIComponent(data)}`
      )
      a.setAttribute('target', '_blank')
      a.setAttribute('dataTrack', `diagram:download-${dataTrack}`)
      a.setAttribute('download', reName)
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
    },
    // 放大
    zoomOut() {
      if (this.newScale > 4) {
        return
      }
      this.newScale = this.newScale + 0.2
      this.bpmnModeler.get('canvas').zoom(this.newScale)
    },
    // 缩小
    zoomIn() {
      if (this.newScale < 0.4) {
        return
      }
      this.newScale = this.newScale - 0.2
      this.bpmnModeler.get('canvas').zoom(this.newScale)
    },
    // 重置
    reset() {
      this.bpmnModeler.get('canvas').zoom('fit-viewport')
    }
  }

}
</script>

<style lang="scss" scoped>
/*左边工具栏以及编辑节点的样式*/
@import "~bpmn-js/dist/assets/diagram-js.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";

html,
body,
#app,
.el-dialog__body,
.containers,
.el-container {
  /*设置内部填充为0，几个布局元素之间没有间距*/
  padding: 0px;
  /*外部间距也是如此设置*/
  margin: 0px;
  /*统一设置高度为100%*/
  height: 100%;
}

.el-header {
  background-color: #b3c0d1;
  color: #333;
  text-align: center;
  line-height: 60px;
}

.el-footer {
  line-height: 18px;
}

.el-aside {
  background-color: #d3dce6;
  color: #333;
  text-align: center;
  display: block;
  // position: relative;
  // overflow-y: scroll;
  height: 600px;
}

.el-main {
  background-color: #e9eef3;
  color: #333;
  text-align: center;
  height: 100%;
}

.containers {
  // position: absolute;
  background-color: #ffffff;
  width: 100%;
  height: 100%;
  .canvas {
    width: 100%;
    height: 100%;
  }
}
</style>
<style lang="scss" >
.bjs-powered-by {
  display: none;
}
</style>

